package ni;

import chatsystemTDa2.*;
import controller.Msg;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;

/**
 * This class handles receiving messages via UDP
 */
public class UDPReceiver extends Thread {

	/**
	 * socket port number
	 * link to the ChatNI
	 * A boolean to colse the thread
	 */
	private final int port = 16050;
	private DatagramSocket sock;
	private byte[] data;
	private ChatNI chatNI;
	private volatile boolean active;
	
	/**
	 * @param chatNI is a link to the chatNI
	 */
	public UDPReceiver(ChatNI chatNI){
		try {
			this.sock = new DatagramSocket(port);
			this.sock.setReceiveBufferSize(2048);
			this.data = new byte[2048];
			this.chatNI = chatNI;
			this.active = true;
		} catch (SocketException e) {e.printStackTrace();
		}
	}
	
	/**
	 * @return the socket
	 */
	public DatagramSocket getSock() { return this.sock; }
	
	/**
	 * Allows closing the thread
	 * this.interrupt() wakes the thread up from sock.receive which is a blocking call
	 * so we can close the thread
	 * we close the socket
	 */
	public void close(){
		this.active = false;
		this.interrupt(); 
		this.sock.close();
		System.out.println("CLOSING");
	}
	
	/**
	 * Receiving messages
	 * We listen on a socket on a specific port
	 * Once receiving, the message is sent to the controller for its processing
	 * Messages are serialized
	 */
	public void run(){
		while(this.active){
				try {
					this.data = new byte[2048];
					DatagramPacket dp = new DatagramPacket(data, data.length);
					sock.receive(dp);
					
					ByteArrayInputStream bais = new ByteArrayInputStream(data);
					BufferedInputStream bis = new BufferedInputStream(bais);
					ObjectInputStream ois = new ObjectInputStream(bis);
					Message obj = (Message) ois.readObject();
					Msg m = new Msg (obj, dp.getAddress());
					
					chatNI.process_receive(m);
					ois.close();
					bis.close();
					bais.close();
				} catch (SocketException e) {
					/**
					 * SocketException can be provoked by the method close(), 
					 * we can close the socket while there is still actions on it
					 * But we know it comes from the method close() if active is false
					 * Then it's not a problem
					*/
					if (this.active == true) {
						e.printStackTrace();
					}
				} catch (IOException e) {e.printStackTrace();
				} catch (ClassNotFoundException e) {e.printStackTrace();} 
		}
	}
}
